﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace UI.Dendrogram
{
    public class CrossDendrogramCanvas:Canvas
    {
        #region const
        private const double LeftDendrogramHeightScale = 0.3;
        private const double TopDendrogramHeightScale = 0.2;
        #endregion

        #region data
        private BinaryHierarchicalData _TopTreeData;
        private BinaryHierarchicalData _LeftTreeData;
        private Dictionary<BinaryHierarchicalData, Branch> _LeftDataBranches;
        private Dictionary<BinaryHierarchicalData, Branch> _TopDataBranches;
        private Dictionary<Branch, BinaryHierarchicalData> _LeftBranchData;
        private Dictionary<Branch, BinaryHierarchicalData> _TopBranchData;
        /// <summary>
        /// left end arm -> top end arm -> heatmapcell
        /// </summary>
        private Dictionary<BranchArm, Dictionary<BranchArm, HeatMapCell>> _TableCells;
        #endregion

        public Dictionary<Branch, BinaryHierarchicalData> TopBranchData
        {
            get { return _TopBranchData; }
        }

        public Dictionary<Branch, BinaryHierarchicalData> LeftBranchData
        {
            get { return _LeftBranchData; }
        }

        public Dictionary<BinaryHierarchicalData, Branch> TopDataBranches
        {
            get { return _TopDataBranches; }
        }

        public Dictionary<BinaryHierarchicalData, Branch> LeftDataBranches
        {
            get { return _LeftDataBranches; }
        }

        public BinaryHierarchicalData LeftTreeData
        {
            get { return _LeftTreeData; }
        }

        public BinaryHierarchicalData TopTreeData
        {
            get { return _TopTreeData; }
        }

        /// <summary>
        /// Row BinaryHierarchicalData ID +":" left(0)/right(1) -> 
        /// Column BinaryHierarchicalData +":" left(0)/right(1) -> cell ID
        /// </summary>
        /// <param name="showTermTree"></param>
        /// <param name="showTermCells"></param>
        /// <param name="leftTreeData"></param>
        /// <param name="topTreeData"></param>
        /// <param name="rowColumnCellMappings"></param>
        /// <param name="cellLabels"></param>
        /// <param name="cellTooltips"></param>
        /// <param name="cellValues"></param>
        public void BindData(
            bool showTermTree, bool showTermCells,
            BinaryHierarchicalData leftTreeData,
            BinaryHierarchicalData topTreeData,
            Dictionary<LeafData, Dictionary<LeafData, int>> rowColumnCellMappings,
            Dictionary<int,string> cellLabels, 
            Dictionary<int,string> cellTooltips, 
            Dictionary<int,double> cellValues)
        {
            this.Children.Clear();
            //this.TestSplitDrawings(leftTreeData);

            this._LeftTreeData = leftTreeData;
            this._TopTreeData = topTreeData;

            double w1 = this.Width * LeftDendrogramHeightScale;
            double h2 = this.Height * TopDendrogramHeightScale;
            double h1 = this.Height - h2;
            double w2 = this.Width - w1;
            
            double scaleX, scaleY;
            Rect leftDendrogramRect = new Rect(0, h2, w1, h1);
            DendrogramUtil.DrawDendrogram(
                this, leftDendrogramRect, Orientation.Horizontal,
                leftTreeData, out scaleX, out scaleY,
                out this._LeftDataBranches, out this._LeftBranchData);

            if (showTermTree)
            {
                Rect topDendrogramRect = new Rect(w1, 0, w2, h2);
                DendrogramUtil.DrawDendrogram(
                    this, topDendrogramRect, Orientation.Vertical,
                    topTreeData, out scaleX, out scaleY,
                    out this._TopDataBranches, out this._TopBranchData);
            }
            this.RegisterBranchEventHandlers();

            if (showTermTree)
            {
                this.DrawHeatmapCells(rowColumnCellMappings, cellLabels, cellTooltips, cellValues);
            }
        }

        private void DrawHeatmapCells(
            Dictionary<LeafData, Dictionary<LeafData, int>> rowColumnCellMappings,
            Dictionary<int, string> cellLabels,
            Dictionary<int, string> cellTooltips,
            Dictionary<int, double> cellValues)
        {
            double min = double.PositiveInfinity;
            double max = double.NegativeInfinity;
            foreach (double val in cellValues.Values)
            {
                if (val < min)
                    min = val;
                if (val > max)
                    max = val;
            }
            List<BinaryHierarchicalData> leftEndTrees = new List<BinaryHierarchicalData>();
            List<BinaryHierarchicalData> topEndTrees = new List<BinaryHierarchicalData>();
            BinaryHierarchicalData leftTreeRoot = null;
            BinaryHierarchicalData topTreeRoot = null;
            List<BinaryHierarchicalData> leftTrees = new List<BinaryHierarchicalData>();
            leftTrees.AddRange(this._LeftDataBranches.Keys);
            List<BinaryHierarchicalData> leftTreeRoots = this.FindRoots(leftTrees);
            Debug.Assert(leftTreeRoots.Count == 1);
            leftTreeRoot = leftTreeRoots[0];
            List<BinaryHierarchicalData> topTrees = new List<BinaryHierarchicalData>();
            topTrees.AddRange(this._TopDataBranches.Keys);
            List<BinaryHierarchicalData> topTreeRoots = this.FindRoots(topTrees);
            Debug.Assert(topTreeRoots.Count == 1);
            topTreeRoot = topTreeRoots[0];
            if (leftTreeRoot.ChildCount > 0)
            {
                foreach (BinaryHierarchicalData tree in leftTreeRoot.Children)
                {
                    if (tree.LeftChild == null && tree.RightChild == null)
                        leftEndTrees.Add(tree);
                }
            }
            else
                leftEndTrees.Add(leftTreeRoot);
            if (topTreeRoot.ChildCount > 0)
            {
                foreach (BinaryHierarchicalData tree in topTreeRoot.Children)
                {
                    if (tree.LeftChild == null && tree.RightChild == null)
                        topEndTrees.Add(tree);
                }
            }
            else
                topEndTrees.Add(topTreeRoot);
            this._TableCells = new Dictionary<BranchArm, Dictionary<BranchArm, HeatMapCell>>();
            foreach (LeafData rowLeaf in rowColumnCellMappings.Keys)
            {
                BinaryHierarchicalData rowData = rowLeaf.Parent;
                Branch rowBranch = this._LeftDataBranches[rowData];
                bool isRowLeft = (rowData.LeftLeaf != null && rowData.LeftLeaf.ID == rowLeaf.ID);
                BranchArm rowArm = new BranchArm { Branch = rowBranch, IsLeft = isRowLeft };
                Dictionary<BranchArm, HeatMapCell> rowCells = new Dictionary<BranchArm, HeatMapCell>();
                foreach (LeafData colLeaf in rowColumnCellMappings[rowLeaf].Keys)
                {
                    BinaryHierarchicalData colData = colLeaf.Parent;
                    Branch colBranch = this._TopDataBranches[colData];
                    bool isColLeft = (colData.LeftLeaf != null && colData.LeftLeaf.ID == colLeaf.ID);
                    BranchArm colArm = new BranchArm { Branch = colBranch, IsLeft = isColLeft };
                    int cellID = rowColumnCellMappings[rowLeaf][colLeaf];
                    string label = cellLabels[cellID];
                    string tooltip = cellTooltips[cellID];
                    double val = cellValues[cellID];
                    Color color = this.GetHeatMapColor(max, min, val);
                    HeatMapCell cell1 =
                        new HeatMapCell(this, rowArm, colArm, color, label, tooltip);
                    rowCells.Add(colArm, cell1);
                }
                this._TableCells.Add(rowArm, rowCells);
            }
        }

        private List<BinaryHierarchicalData> FindRoots(List<BinaryHierarchicalData> allTreeData)
        {
            List<BinaryHierarchicalData> roots=new List<BinaryHierarchicalData>();
            foreach(BinaryHierarchicalData treeData in allTreeData)
            {
                BinaryHierarchicalData parent = treeData;
                while(parent.Parent !=null)
                    parent = parent.Parent;
                if(!roots.Contains(parent))
                    roots.Add(parent);
            }
            return roots;
        }

        private Color GetHeatMapColor(double max, double min, double value)
        {
            if(max==min)
                return Colors.Gray;
            double intensity = (value - min)/(max - min);
            byte redness = (byte) (intensity*220);
            byte blueness = (byte) (220 - 220*intensity);
            return Color.FromArgb(255, redness, 0, blueness);
        }

        #region interaction
        private void RegisterBranchEventHandlers()
        {
            if (this._LeftBranchData != null && this._LeftBranchData.Count > 0)
            {
                foreach (Branch leftBranch in this._LeftBranchData.Keys)
                {
                    leftBranch.BranchUnSelected += new OnBranchUnSelected(LeftBranchUnSelected);
                    leftBranch.BranchSelected += new OnBranchSelected(LeftBranchSelected);
                    leftBranch.BranchExpanded += new OnBranchExpanded(LeftBranchExpanded);
                    leftBranch.BranchCollapsed += new OnBranchCollapsed(LeftBranchCollapsed);
                }
            }

            if (this._TopBranchData != null && this._TopBranchData.Count > 0)
            {
                foreach (Branch topBranch in this._TopBranchData.Keys)
                {
                    topBranch.BranchUnSelected += new OnBranchUnSelected(topBranch_BranchUnSelected);
                    topBranch.BranchSelected += new OnBranchSelected(topBranch_BranchSelected);
                    topBranch.BranchExpanded += new OnBranchExpanded(topBranch_BranchExpanded);
                    topBranch.BranchCollapsed += new OnBranchCollapsed(topBranch_BranchCollapsed);
                }
            }
        }

        void topBranch_BranchCollapsed(Branch branch)
        {
            CollapseTopBranch(branch);
        }

        void topBranch_BranchExpanded(Branch branch)
        {
            ExpandTopBranch(branch);
        }

        void topBranch_BranchSelected(Branch branch)
        {
            SelectTopBranch(branch);
        }

        void topBranch_BranchUnSelected(Branch branch)
        {
            UnSelectTopBranch(branch);
        }

        private void CollapseTopBranch(Branch branch)
        {
            //this.UnSelectBranch(this._Root);
            branch.Collapsed = true;
            BinaryHierarchicalData treeData = branch.Data;
            if (treeData.LeftChild != null)
            {
                Branch leftBranch = this._TopDataBranches[treeData.LeftChild];
                this.CollapseTopBranch(leftBranch);
                leftBranch.Visibility = Visibility.Collapsed;
            }
            if (treeData.RightChild != null)
            {
                Branch rightBranch = this._TopDataBranches[treeData.RightChild];
                this.CollapseTopBranch(rightBranch);
                rightBranch.Visibility = Visibility.Collapsed;
            }
        }

        private void ExpandTopBranch(Branch branch)
        {
            //this.UnSelectBranch(this._Root);
            branch.Collapsed = false;
            BinaryHierarchicalData treeData = branch.Data;
            if (treeData.LeftChild != null)
            {
                Branch leftBranch = this._TopDataBranches[treeData.LeftChild];
                this.ExpandTopBranch(leftBranch);
                leftBranch.Visibility = Visibility.Visible;
            }
            if (treeData.RightChild != null)
            {
                Branch rightBranch = this._TopDataBranches[treeData.RightChild];
                this.ExpandTopBranch(rightBranch);
                rightBranch.Visibility = Visibility.Visible;
            }
        }

        private void UnSelectTopBranch(Branch branch)
        {
            branch.Selected = false;
            BinaryHierarchicalData treeData = branch.Data;
            if (treeData.LeftChild != null)
            {
                Branch leftBranch = this._TopDataBranches[treeData.LeftChild];
                this.UnSelectTopBranch(leftBranch);
            }
            if (treeData.RightChild != null)
            {
                Branch rightBranch = this._TopDataBranches[treeData.RightChild];
                this.UnSelectTopBranch(rightBranch);
            }
        }

        private void SelectTopBranch(Branch branch)
        {
            // this.UnSelectBranch(this._Root);
            branch.Selected = true;
            BinaryHierarchicalData treeData = (BinaryHierarchicalData)branch.Data;
            if (treeData.LeftChild != null)
            {
                Branch leftBranch = this._TopDataBranches[treeData.LeftChild];
                this.SelectTopBranch(leftBranch);
            }
            if (treeData.RightChild != null)
            {
                Branch rightBranch = this._TopDataBranches[treeData.RightChild];
                this.SelectTopBranch(rightBranch);
            }
        }

        void LeftBranchUnSelected(Branch branch)
        {
            this.UnSelectLeftBranch(branch);
        }

        void LeftBranchCollapsed(Branch branch)
        {
            this.CollapseLeftBranch(branch);
        }

        void LeftBranchExpanded(Branch branch)
        {
            this.ExpandLeftBranch(branch);
        }

        void LeftBranchSelected(Branch branch)
        {
            this.SelectLeftBranch(branch);
        }

        private void CollapseLeftBranch(Branch branch)
        {
            //this.UnSelectBranch(this._Root);
            branch.Collapsed = true;
            BinaryHierarchicalData treeData = branch.Data;
            if (treeData.LeftChild != null)
            {
                Branch leftBranch = this._LeftDataBranches[treeData.LeftChild];
                this.CollapseLeftBranch(leftBranch);
                leftBranch.Visibility = Visibility.Collapsed;
            }
            if (treeData.RightChild != null)
            {
                Branch rightBranch = this._LeftDataBranches[treeData.RightChild];
                this.CollapseLeftBranch(rightBranch);
                rightBranch.Visibility = Visibility.Collapsed;
            }
        }

        private void ExpandLeftBranch(Branch branch)
        {
            //this.UnSelectBranch(this._Root);
            branch.Collapsed = false;
            BinaryHierarchicalData treeData = branch.Data;
            if (treeData.LeftChild != null)
            {
                Branch leftBranch = this._LeftDataBranches[treeData.LeftChild];
                this.ExpandLeftBranch(leftBranch);
                leftBranch.Visibility = Visibility.Visible;
            }
            if (treeData.RightChild != null)
            {
                Branch rightBranch = this._LeftDataBranches[treeData.RightChild];
                this.ExpandLeftBranch(rightBranch);
                rightBranch.Visibility = Visibility.Visible;
            }
        }

        private void UnSelectLeftBranch(Branch branch)
        {
            branch.Selected = false;
            BinaryHierarchicalData treeData = branch.Data;
            if (treeData.LeftChild != null)
            {
                Branch leftBranch = this._LeftDataBranches[treeData.LeftChild];
                this.UnSelectLeftBranch(leftBranch);
            }
            if (treeData.RightChild != null)
            {
                Branch rightBranch = this._LeftDataBranches[treeData.RightChild];
                this.UnSelectLeftBranch(rightBranch);
            }
        }

        private void SelectLeftBranch(Branch branch)
        {
            // this.UnSelectBranch(this._Root);
            branch.Selected = true;
            BinaryHierarchicalData treeData = (BinaryHierarchicalData)branch.Data;
            if (treeData.LeftChild != null)
            {
                Branch leftBranch = this._LeftDataBranches[treeData.LeftChild];
                this.SelectLeftBranch(leftBranch);
            }
            if (treeData.RightChild != null)
            {
                Branch rightBranch = this._LeftDataBranches[treeData.RightChild];
                this.SelectLeftBranch(rightBranch);
            }
        }
        #endregion
    }
}
